package base

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gomodule/redigo/redis"
	log "manager/collector/logger"
	"reflect"
)

// RedisBase 项目通用redis数据
type RedisBase struct{}

// PrefixName redis 前缀名称
func (r *RedisBase) PrefixName() (hashName string) {
	return "manager"
}

// Clear 清理
func (r *RedisBase) Clear() {
}

// RedisOperator redis 操作
type RedisOperator struct {
	RedisBase
	redisClient *RedisClient
}

func NewRedisOperator(client *RedisClient) *RedisOperator {
	r := &RedisOperator{
		redisClient: client,
	}
	r.Clear()
	return r
}

func (r *RedisOperator) GetRedisClient() *RedisClient {
	return r.redisClient
}

// GetValByKey 依据KEY获取REDIS数据
func (r *RedisOperator) GetValByKey(ctx context.Context, key string) string {
	return r.redisClient.GetValByKey(ctx, key)
}

// AddValue 添加值
func (r *RedisOperator) AddValue(ctx context.Context, groupName, key string, value interface{}) (err error) {
	key = fmt.Sprintf("%v:%v:%v", r.PrefixName(), groupName, key)
	rKind := reflect.TypeOf(value).Kind()
	if rKind != reflect.String {
		data, _err := json.Marshal(value)
		if _err != nil {
			err = _err
			return
		}

		r.redisClient.SetRedisItem(ctx, key, string(data))
		return
	}

	r.redisClient.SetRedisItem(ctx, key, fmt.Sprintf("%v", value))
	return
}

// AddValueWithExpireTime 添加值
func (r *RedisOperator) AddValueWithExpireTime(ctx context.Context, groupName, key string, value interface{}, expiredTime int64) (err error) {
	key = fmt.Sprintf("%v:%v:%v", r.PrefixName(), groupName, key)
	rKind := reflect.TypeOf(value).Kind()
	if rKind != reflect.String {
		data, _err := json.Marshal(value)
		if _err != nil {
			err = _err
			return
		}

		r.redisClient.SetRedisItemWithExpireTime(ctx, key, string(data), expiredTime)
		return
	}

	r.redisClient.SetRedisItemWithExpireTime(ctx, key, fmt.Sprintf("%v", value), expiredTime)
	return
}

func (r *RedisOperator) ExistValue(ctx context.Context, groupName, key string) bool {
	return r.redisClient.ExistsKey(ctx, fmt.Sprintf("%v:%v:%v", r.PrefixName(), groupName, key))
}

// GetValue 获取值
func (r *RedisOperator) GetValue(ctx context.Context, groupName, key string, out interface{}) (err error) {
	key = fmt.Sprintf("%v:%v:%v", r.PrefixName(), groupName, key)
	if !r.redisClient.ExistsKey(ctx, key) {
		return fmt.Errorf("key not exist")
	}

	if value := r.redisClient.GetValByKey(ctx, key); value == "" {
		return
	} else {
		err = json.Unmarshal([]byte(value), out)
		if err != nil {
			*(out.(*string)) = value
			err = nil
		}
	}
	return
}

// DelValue 删除值
func (r *RedisOperator) DelValue(ctx context.Context, groupName, key string) (err error) {
	key = fmt.Sprintf("%v:%v:%v", r.PrefixName(), groupName, key)
	if !r.redisClient.ExistsKey(ctx, key) {
		return fmt.Errorf("key not exist")
	}

	if !r.redisClient.DeletedRedisItem(ctx, key) {
		log.Errorf("DelValue failed key:%v", key)
		return fmt.Errorf("DelValue failed")
	}
	return
}

// DeletedRedisItem 删除redis item
func (r *RedisOperator) DeletedRedisItem(ctx context.Context, key string) bool {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.DeletedRedisItem(ctx, key)
}

// BatchDel 批量删除
func (r *RedisOperator) BatchDel(ctx context.Context, keys []string) (count int, err error) {
	return r.redisClient.BatchDel(ctx, keys)
}

// BatchAdd 批量添加
func (r *RedisOperator) BatchAdd(ctx context.Context, filed map[string]string) (err error) {
	if len(filed) == 0 {
		return
	}
	if !r.redisClient.Mset(ctx, filed) {
		return fmt.Errorf("set failed")
	}
	return
}

// AddHash 添加值
func (r *RedisOperator) AddHash(ctx context.Context, hashName, key string, value interface{}) (err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	rKind := reflect.TypeOf(value).Kind()
	if rKind != reflect.String {
		data, _err := json.Marshal(value)
		if _err != nil {
			err = _err
			return
		}

		if !r.redisClient.SetHashItem(ctx, hashName, key, string(data)) {
			log.Errorf("AddHash failed key:%v", key)
		}
		return
	}

	if !r.redisClient.SetHashItem(ctx, hashName, key, fmt.Sprintf("%v", value)) {
		log.Errorf("AddHash failed key:%v", key)
	}

	return
}

// DelHash 删除值
func (r *RedisOperator) DelHash(ctx context.Context, hashName, key string) (err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	if !r.redisClient.ExistsHashItem(ctx, hashName, key) {
		return
	}

	if !r.redisClient.DelHashItem(ctx, hashName, key) {
		log.Errorf("DelHash failed key:%v", key)
	}
	return
}

// GetHash 获取hash值
func (r *RedisOperator) GetHash(ctx context.Context, hashName, key string, out interface{}) (err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	if !r.redisClient.ExistsHashItem(ctx, hashName, key) {
		return errors.New("record not found")
	}

	if value := r.redisClient.GetHashItem(ctx, hashName, key); value == "" {
		return
	} else {
		err = json.Unmarshal([]byte(value), out)
		if err != nil {
			err = nil
			*(out.(*string)) = value
		}
	}

	return
}

// HMGet 获取hash值
func (r *RedisOperator) HMGet(ctx context.Context, hashName string, key []string) (values map[string]string, err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	values, err = r.redisClient.Hmget(ctx, hashName, key...)
	return
}

// HGetAll 获取所有
func (r *RedisOperator) HGetAll(ctx context.Context, hashName string) (values map[string]string, err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	values, err = r.redisClient.HGetAll(ctx, hashName)
	return
}

// HMSet 设置值
func (r *RedisOperator) HMSet(ctx context.Context, hashName string, fieldValue map[interface{}]interface{}) (err error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	return r.redisClient.Hmset(ctx, hashName, fieldValue)
}

// AddList 添加到列表
func (r *RedisOperator) AddList(ctx context.Context, listName string, value string) bool {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.PushItemToList(ctx, listName, value)
}

// BatchAddList 添加列表
func (r *RedisOperator) BatchAddList(ctx context.Context, listName string, value ...string) bool {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.PushItemToList(ctx, listName, value...)
}

// DelList 删除列表数据
func (r *RedisOperator) DelList(ctx context.Context, listName string, value string) bool {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.RemoveItemFromList(ctx, listName, value)
}

// PopFormList 获取列表中的第一个数据
func (r *RedisOperator) PopFormList(ctx context.Context, listName string) (value string) {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	var err error
	value, err = redis.String(r.redisClient.Do(ctx, "LPOP", listName))
	if err != nil {
		log.Error("QUEYE LPUSH：" + listName + ";" + err.Error())
		return
	}
	return
}

// BPopFormList 获取列表中的第一个数据
func (r *RedisOperator) BPopFormList(ctx context.Context, listName string) (value string) {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	var err error
	values, err := redis.Strings(r.redisClient.Do(ctx, "BLPOP", listName, 100))
	if err != nil && err.Error() != "redigo: nil returned" {
		log.Error("QUEYE LPUSH：" + listName + ";" + err.Error())
		return
	}

	if len(values) == 2 {
		return values[1]
	}
	return
}

// GetFirstFormList 获取列表中的第一个数据
func (r *RedisOperator) GetFirstFormList(ctx context.Context, listName string) (value string) {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.GetFirstItemFromList(ctx, listName)
}

// GetItemFromList 获取元素
func (r *RedisOperator) GetItemFromList(ctx context.Context, listName string, item string) (value string) {
	return
}

// GetRangeFromQueue 获取列表
func (r *RedisOperator) GetRangeFromQueue(ctx context.Context, listName string, count int64) []string {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.GetRangeFromQueue(ctx, listName, count)
}

func (r *RedisOperator) ListRange(ctx context.Context, listName string, from, to int64) []string {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	return r.redisClient.ListRange(ctx, listName, from, to)
}

// LenList 列表长度
func (r *RedisOperator) LenList(ctx context.Context, listName string) (length int64, err error) {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	length, err = redis.Int64(r.redisClient.Do(ctx, "LLEN", listName))
	if err != nil {
		log.Error("QUEYE LPUSH：" + listName + ";" + err.Error())
		return
	}
	return
}

// AddListToTail 前插
func (r *RedisOperator) AddListToTail(ctx context.Context, listName string, value string) (err error) {
	listName = fmt.Sprintf("%v:%v", r.PrefixName(), listName)
	_, err = r.redisClient.Do(ctx, "LPUSH", listName, value)
	if err != nil {
		log.Error("QUEYE LPUSH：" + listName + ";" + value + err.Error())
		return err
	}
	return
}

// Expire 设置过期时间
func (r *RedisOperator) Expire(ctx context.Context, key string, seconds int32) (err error) {
	if !r.redisClient.Expire(ctx, key, seconds) {
		return fmt.Errorf("set expire failed")
	}
	return
}

// ExistsKey 是否存在
func (r *RedisOperator) ExistsKey(ctx context.Context, groupName, key string) bool {
	_key := r.PrefixName()
	if groupName != "" {
		_key = fmt.Sprintf("%v:%v", _key, groupName)
	}

	_key = fmt.Sprintf("%v:%v", _key, key)
	return r.redisClient.ExistsKey(ctx, _key)
}

// Hincrby 设置过期时间
func (r *RedisOperator) Hincrby(ctx context.Context, hashName string, key string, increment interface{}) (int64, error) {
	hashName = fmt.Sprintf("%v:%v", r.PrefixName(), hashName)
	return r.redisClient.Hincrby(ctx, hashName, key, increment)
}

// MatchDel 模糊删除
func (r *RedisOperator) MatchDel(conn redis.Conn, prefixKey string) (err error) {
	defer conn.Close()
	// 用于循环查询KEY
	iter := 0
	var keys []string
	for {
		if arr, _err := redis.Values(conn.Do("SCAN", iter, "MATCH", prefixKey)); err != nil {
			return _err
		} else {
			iter, _ = redis.Int(arr[0], nil)
			key, _ := redis.Strings(arr[1], nil)
			keys = append(keys, key...)
		}
		if iter == 0 {
			break
		}
	}
	// 开启事务
	conn.Send("MULTI")
	for _, value := range keys {
		// 删除
		conn.Send("DEL", value)
	}
	_, err = redis.Values(conn.Do("EXEC"))
	if err != nil {
		log.Error("err:", err)
	}
	return
}

// Mulit 事务开启
func (r *RedisOperator) Mulit(conn redis.Conn) (err error) {
	_, err = conn.Do("multi")
	return
}

// DisCard 事务取消
func (r *RedisOperator) DisCard(conn redis.Conn) (err error) {
	_, err = conn.Do("discard")
	return
}

// Exec 事务执行
func (r *RedisOperator) Exec(conn redis.Conn) (err error) {
	_, err = conn.Do("exec")
	return
}

// MatchCount 模糊查询数量
func (r *RedisOperator) MatchCount(conn redis.Conn, prefixKey string) (count int64, err error) {
	iter := 0
	for {
		if arr, _err := redis.Values(conn.Do("SCAN", iter, "MATCH", prefixKey)); err != nil {
			return 0, _err
		} else {
			iter, _ = redis.Int(arr[0], nil)
			key, _ := redis.Strings(arr[1], nil)
			for _, v := range key {
				if v != "" {
					count++
				}
			}
		}
		if iter == 0 {
			break
		}
	}
	return
}

// GetBit 获取bit
func (r *RedisOperator) GetBit(ctx context.Context, key string, offset int) (value int, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.GetBit(ctx, key, offset)
}

// SetBitWithExpireTime 设置bit
func (r *RedisOperator) SetBitWithExpireTime(ctx context.Context, key string, offset, value int, expiredTime int64) (err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	err = r.redisClient.SetBit(ctx, key, offset, value)
	if err != nil {
		return
	}

	r.redisClient.SetRedisItemWithExpireTime(ctx, key, key, expiredTime)
	return
}

// SetBit 设置bit
func (r *RedisOperator) SetBit(ctx context.Context, key string, offset, value int) (err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.SetBit(ctx, key, offset, value)
}

// MGetBit 批量获取
func (r *RedisOperator) MGetBit(ctx context.Context, key string, offsets []int) (value []int, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.MGetBit(ctx, key, offsets)
}

func (r *RedisOperator) ZAdd(ctx context.Context, key string, score int64, member string) (value int64, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.Zadd(ctx, key, score, member)
}

func (r *RedisOperator) ZRem(ctx context.Context, key, member string) (value int64, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.Zrem(ctx, key, member)
}

func (r *RedisOperator) ZRevRange(ctx context.Context, key string, start, stop int64) (valSlice []string, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.ZRevRangeWithScore(ctx, key, start, stop)
}

func (r *RedisOperator) ZRange(ctx context.Context, key string, start, stop int64) (valSlice []string, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.ZRangeWithScore(ctx, key, start, stop)
}

func (r *RedisOperator) ZBatchAdd(ctx context.Context, key string, scoreMemberPairs []string) (value int64, err error) {
	key = fmt.Sprintf("%v:%v", r.PrefixName(), key)
	return r.redisClient.ZaddBatch(ctx, key, scoreMemberPairs)
}
